#include "SkyboxProcess.h"
#include "Utils/LogUtil.h"
#include "Game/GameState.h"
#include "Render/RenderState.h"
#include "Core/CoreGlobals.h"
#include "Game/Scene/Entity.h"
#include "Game/Component/Model.h"
#include "Render/Buffer/VertexBuffer.h"
#include "Render/Buffer/IndexBuffer.h"
#include "Render/Buffer/CustomBuffer.h"
#include "../LocalShader.h"
#include "../LocalScene.h"
#include "../ResourceProcess.h"

void SkyboxProcess::OnInit()
{
	mLocalScene = static_cast<LocalScene*>(mGameScene);
	mResourceProcess = mLocalScene->GetProcess<ResourceProcess>("ResourceProcess");

	float cubeSize = mGameScene->GetCamera().GetFarZ();

	ENQUEUE_RENDER_COMMAND(BuildModel)([this, cubeSize](VkCommandBuffer commandBuffer)
		{
			GeometryGenerator geometryGenerator;
			GeometryGenerator::MeshData cubeData = geometryGenerator.CreateCube(cubeSize, cubeSize, cubeSize, 0);

			std::vector<glm::vec3> vertexs;
			vertexs.resize(cubeData.Vertices.size());
			for (uint32 i = 0; i < cubeData.Vertices.size(); ++i)
			{
				vertexs[i] = cubeData.Vertices[i].Position;
			}

			mVertexBuffer = VertexBuffer::Builder()
				.SetData(vertexs.data())
				.SetSize(vertexs.size() * sizeof(glm::vec3))
				.Build();

			mIndexBuffer = IndexBuffer::Builder()
				.SetData(cubeData.Indices32.data())
				.SetSize(cubeData.Indices32.size() * sizeof(uint32))
				.SetIndexCount(cubeData.Indices32.size())
				.Build();

			RefCountPtr<RHITexture> cubemapImage = GetTexture("ResourceProcess", "cubemapImage");
			RefCountPtr<RHISampler> cubemapSampler = GetSampler("ResourceProcess", "cubemapSampler");

			mDescriptor = RHIDescriptor::Builder()
				.PushShader<SkyboxVertexShader>()
				.PushShader<SkyboxFragmentShader>()
				.SetDescriptorCount(0, 1)
				.Build();

			mDescriptor->SetValue("cubemapImage", cubemapImage, cubemapSampler);
			mDescriptorMask = mDescriptor->AllocateDescriptor();

			RefCountPtr<ColorRenderTarget> deferredImage = GetTexture("DeferredProcess", "deferredImage");
			deferredImage->ResetLoadOp(VK_ATTACHMENT_LOAD_OP_LOAD);

			RefCountPtr<DepthRenderTarget> gBufferDepth0 = GetTexture("GBufferProcess", "gBufferDepth_s");
			RefCountPtr<DepthRenderTarget> gBufferDepth1 = GetTexture("GBufferProcess", "gBufferDepth_d");

			gBufferDepth0->ResetLoadOp(VK_ATTACHMENT_LOAD_OP_LOAD);
			gBufferDepth1->ResetLoadOp(VK_ATTACHMENT_LOAD_OP_LOAD);

			RefCountPtr<ColorBlendAttachment> colorBlendAttachment = ColorBlendAttachment::Builder()
				.SetBlendEnable(VK_TRUE)
				.Build();

			mPipeline = GraphicsPipeline::Builder()
				.SetDescriptor(mDescriptor)
				.AddColorRenderTarget(deferredImage)
				.SetDepthRenderTarget(gBufferDepth0)
				.SetDstStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
				.SetDstAccessMask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)
				.SetSrcStageMask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
				.SetSrcAccessMask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)
				.SetCullMode(VK_CULL_MODE_BACK_BIT)
				.SetDepthTestEnable(VK_TRUE)
				.SetDepthWriteEnable(VK_FALSE)
				.SetDepthCompareOp(VK_COMPARE_OP_LESS_OR_EQUAL)
				.SetDepthBoundsTestEnable(VK_FALSE)
				.SetStencilTestEnable(VK_FALSE)
				.AddColorBlendAttachment(colorBlendAttachment)
				.SetLogicOpEnable(VK_FALSE)
				.SetLogicOp(VK_LOGIC_OP_COPY)
				.SetDepthClampEnable(VK_FALSE)
				.SetRasterizerDiscardEnable(VK_FALSE)
				.SetDepthBiasEnable(VK_FALSE)
				.Build();

			mRenderPass = RHIRenderPass::Builder()
				.AddRenderTarget(deferredImage)
				.AddRenderTarget(gBufferDepth0)
				.AddPipeline(mPipeline)
				.SetSrcStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
				.SetSrcAccessMask(VK_ACCESS_SHADER_READ_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
				.SetDstAccessMask(VK_ACCESS_SHADER_READ_BIT)
				.Build(this, "RenderPass");

			mFrameBuffer[0] = RHIFrameBuffer::Builder()
				.SetRenderPass(mRenderPass)
				.AddRenderTarget(deferredImage)
				.AddRenderTarget(gBufferDepth0)
				.Build();

			mFrameBuffer[1] = RHIFrameBuffer::Builder()
				.SetRenderPass(mRenderPass)
				.AddRenderTarget(deferredImage)
				.AddRenderTarget(gBufferDepth1)
				.Build();
		});
}

void SkyboxProcess::OnTick(float deltaTime)
{
	if (GGameState->GetFrameAmount() < 2)
		return;

	PushConstants pushConstants;
	pushConstants.proj = mGameScene->GetCamera().GetProj();
	pushConstants.view = mGameScene->GetCamera().GetView();

	ENQUEUE_RENDER_COMMAND(RecordCommand)([this, pushConstants](VkCommandBuffer commandBuffer)
		{
			VkRenderPassBeginInfo renderPassBeginInfo = mFrameBuffer[mResourceProcess->mCurrMask]->GetRenderPassBeginInfo();

			vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);

			vkCmdSetViewport(commandBuffer, 0, 1, &GRenderState->GetViewport());
			vkCmdSetScissor(commandBuffer, 0, 1, &GRenderState->GetScissor());

			vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline->GetPipeline());

			vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline->GetPipelineLayout(), 0, 1, &mDescriptorMask.GetDescriptorSet(), 0, nullptr);

			PushConstants realPushConstants = pushConstants;
			vkCmdPushConstants(commandBuffer, mPipeline->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants), &realPushConstants);

			VkBuffer vertexBuffer = mVertexBuffer->GetBuffer();
			VkDeviceSize offsets = 0;
			vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, &offsets);

			VkBuffer indexBuffer = mIndexBuffer->GetBuffer();
			vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32);

			vkCmdDrawIndexed(commandBuffer, mIndexBuffer->GetIndexCount(), 1, 0, 0, 0);

			vkCmdEndRenderPass(commandBuffer);
		});
}

void SkyboxProcess::OnResize()
{
	RefCountPtr<ColorRenderTarget> deferredImage = GetTexture("DeferredProcess", "deferredImage");

	RefCountPtr<DepthRenderTarget> gBufferDepth0 = GetTexture("GBufferProcess", "gBufferDepth_s");
	RefCountPtr<DepthRenderTarget> gBufferDepth1 = GetTexture("GBufferProcess", "gBufferDepth_d");

	mFrameBuffer[0] = RHIFrameBuffer::Builder()
		.SetRenderPass(mRenderPass)
		.AddRenderTarget(deferredImage)
		.AddRenderTarget(gBufferDepth0)
		.Build();

	mFrameBuffer[1] = RHIFrameBuffer::Builder()
		.SetRenderPass(mRenderPass)
		.AddRenderTarget(deferredImage)
		.AddRenderTarget(gBufferDepth1)
		.Build();
}
